<?php

namespace Aitels\Foundation\Code;


class ClassBuilder
{
    const PUBLIC = 1;
    const PRIVATE = 2;
    const PROTECTED = 4;
    const STATIC = 8;
    protected $namespace;
    protected $uses = [];
    protected $classDoc = [];
    protected $className;
    protected $parent;
    protected $fields = [];
    protected $methods = [];
    protected $content;
    protected $traits = [];


    public function print()
    {
        echo $this->build();
    }


    public function build()
    {
        $this->init();
        $this->buildNamespace();
        $this->buildClassDoc();
        $this->buildClassName();
        $this->buildExtends();
        $this->buildUses();
        $this->buildUseTraits();
        $this->buildFields();
        $this->buildMethods();
        return $this->content;
    }


    public function setNamespace($namespace)
    {
        $namespace = ltrim($namespace, '\\');
        $this->namespace = $namespace;

    }

    public function addClassDoc($doc)
    {
        $this->classDoc[] = $doc;
    }

    public function setClassName($className)
    {
        $this->className = $className;
    }

    public function addUse($class)
    {
        $this->uses[] = $class;
    }

    public function setExtends($parent)
    {
        $this->parent = $parent;
    }


    public function addMethod($method, $code, $modifier, $params = null)
    {
        $this->methods[$method]['code'] = $code;
        $this->methods[$method]['modifier'] = $modifier;
        $this->methods[$method]['params'] = $params;
    }


    public function addMethodDoc($method, $comment)
    {
        $this->methods[$method]['comment'][] = $comment;
    }

    public function addTrait($trait)
    {
        $this->traits[] = $trait;
    }


    public function addField($fieldName, $modifier)
    {
        $this->fields[$fieldName]['modifier'] = $modifier;
    }

    public function addFieldDoc($fieldName, $comment)
    {
        $this->fields[$fieldName]['comment'][] = $comment;
    }


    protected function buildNamespace()
    {
        $this->content = str_replace('@namespace', $this->namespace, $this->content);
    }

    protected function buildClassDoc()
    {
        if ($this->classDoc) {
            $template = '/**'.PHP_EOL;
            foreach ($this->classDoc as $doc) {
                $template .= $this->space(1).'*'.$doc.PHP_EOL;
            }
            $template .= $this->space(1).'*/';
            $this->content = str_replace('@classDoc', $template, $this->content);
        } else {
            $this->content = str_replace('@classDoc', '', $this->content);
        }
    }

    protected function buildClassName()
    {
        $this->content = str_replace('@className', $this->className, $this->content);
    }

    protected function buildExtends()
    {
        if ($this->parent) {
            $this->content = str_replace('@parent', 'extends '.$this->parent, $this->content);
        } else {
            $this->content = str_replace('@parent', '', $this->content);
        }
    }

    protected function buildUses()
    {
        $block = '';
        foreach ($this->uses as $use) {
            $block .= "use $use;".PHP_EOL;
        }

        $this->content = str_replace('@uses', $block, $this->content);
    }

    protected function buildUseTraits()
    {
        $block = '';

        foreach ($this->traits as $trait) {
            $block .= $this->space(4)."use $trait;".PHP_EOL;
        }

        $this->content = str_replace('@useTraits', $block, $this->content);
    }

    protected function buildFields()
    {
        $block = '';

        foreach ($this->fields as $name => $definition) {
            $access = $this->getAccess($definition['modifier']);
            $static = $this->getStatic($definition['modifier']) ? $this->getStatic($definition['modifier']).' ' : ' ';
            $block .= $this->space(4)."$access$static\$$name;".PHP_EOL;
        }

        $this->content = str_replace('@fields', $block, $this->content);

    }


    protected function buildMethods()
    {
        $block = '';
        foreach ($this->methods as $name => $definition) {
            if (isset($definition['comment'])) {
                $block .= $this->buildMethodDoc($name);
            }

            $block .= $this->methodHead($name, $definition['modifier'], $definition['params'])
                .$this->methodBody($definition['code'])
                .$this->methodTail();
        }

        $this->content = str_replace('@methods', $block, $this->content);
    }


    protected function buildMethodDoc($methodName)
    {
        $block = $this->space(4).'/**'.PHP_EOL;
        foreach ($this->methods[$methodName]['comment'] as $comment) {
            $block .= $this->space(5)."*$comment".PHP_EOL;
        }
        $block .= $this->space(4).'*/'.PHP_EOL;
        return $block;
    }


    protected function methodHead($name, $modifier, $params)
    {
        $access = $this->getAccess($modifier);
        $static = $this->getStatic($modifier);

        return $this->space(4)."$access$static function $name($params)".PHP_EOL
            .$this->space(4).'{'.PHP_EOL;
    }

    protected function methodBody($code)
    {
        return $this->space(8).$code.PHP_EOL;
    }

    protected function methodTail()
    {
        return $this->space(4).'}'.PHP_EOL.PHP_EOL;
    }


    protected function getAccess($modifier)
    {
        switch (true) {
            case $modifier & self::PUBLIC:
                return 'public';
            case $modifier & self::PRIVATE:
                return 'private';
            case $modifier & self::PROTECTED:
                return 'protected';
        }
    }

    protected function getStatic($modifier)
    {
        return $modifier & self::STATIC ? ' static' : '';
    }


    protected function space($quantity)
    {
        return str_repeat(' ', $quantity);
    }

    protected function init()
    {
        $this->content = file_get_contents(__DIR__.'/Stubs/class.stub');
    }


}
